from aiohttp import web
import aiohttp
import requests
import time
import json
import os
import csv
import random
from datetime import datetime, timedelta
from io import BytesIO
from PIL import Image
import logging
from threading import Thread
from param import *
from publib import setup_logger, get_logger, csv_read_list, csv_write_list

cur_file = os.path.basename(__file__).split(".")[0]
setup_logger(f"logs/{cur_file}.log")
logger = get_logger(__name__)

config = {}
if (os.path.exists("config.json")):
    with open("config.json", "r") as f:
        config = json.load(f)

disallow_type = ["Content-Encoding", "Content-Length", "Date", "Server", "Connection", "Transfer-Encoding", "Access-Control-Allow-Origin", 
                 "Access-Control-Allow-Methods", "Access-Control-Allow-Headers", "Strict-Transport-Security"]

proxy = None
    
def _audio_csv_2_json(csv_data):
    if (len(csv_data) < 4):
        pic = ""
    else:
        pic = csv_data[3]
    info = {
        "name": csv_data[0],
        "artists": csv_data[1],
        "url": csv_data[2],
        "pic": pic,
        "mv": "",
    }
    return info

msuic_src = ["qq", "kg", "kw", "wy"]

"""
成功消息示例
{
    "code": 0,
    "msg": "success", 
    "url": "http://fs.youthandroid2.kugou.com/xxxxx.mp3'
}
"""
music_err_url_rsp = {
    "code": 1,
    "msg": "未知错误"
}

def _music_url_api_1(src, id, name='', artist='', quality='320k'):
    # 音乐源1
    url = f"https://lxmusicapi.onrender.com/url/{src}/{id}/{quality}"
    headers = {
        "X-Request-Key": "share-v2"
    }
    try:
        logger.info(f"request: {url}")
        data = requests.get(url, headers=headers, timeout=3).json()
    except:
        data = music_err_url_rsp
    return data

# api.cenguigui.cn  wy
def _music_url_api_2(src, id, name='', artist='', quality='320k'):
    src_table = {
        # 酷我貌似被拉黑，返回的都是同一首歌
        # "kw": {
        #     "api": "kuwo",
        #     "quality": {
        #         "320k": "exhigh",
        #         "flac": "lossless"
        #     }
        # },
        "wy": {
            "api": "netease/music_v1.php",
            "quality": {
                "128k": "standard",
                "320k": "exhigh",
                "flac": "lossless"
            }
        }
    }

    api = src_table.get(src, {}).get("api", "")
    quality_table = src_table.get(src, {}).get("quality", {})

    if (src not in src_table or quality not in quality_table): return music_err_url_rsp

    # 音乐源1
    url = f"https://api.cenguigui.cn/api/{api}?id={id}&level={quality_table[quality]}"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
    }
    try:
        logger.info(f"request: {url}")
        _ = requests.get(url, headers=headers, timeout=3).json() 
        data = {
            "code": 0,
            "msg": "success", 
            "url": _.get("data", {}).get("url", ""),
            "pic": _.get("data", {}).get("pic", ""),
            "mv": _.get("data", {}).get("mv_info", {}).get("mv", ""),
        }
    except:
        data = music_err_url_rsp
    return data

# api.xingzhige.com  tx
def _music_url_api_3(src, id, name='', artist='', quality='320k'):
    # 音乐源3
    src_table = {
        "tx": "QQmusicVIP",
    }

    quality_table = {
        "128k": "7",
        "320k": "8",
        "flac": "11"
    }

    if (src not in src_table or quality not in quality_table): return music_err_url_rsp

    # 音乐源1
    url = f"https://api.xingzhige.com/API/{src_table[src]}?mid={id}&br={quality_table[quality]}"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
    }
    try:
        logger.info(f"request: {url}")
        _ = requests.get(url, headers=headers, timeout=3).json()
        data = {
            "code": 0,
            "msg": "success", 
            "url": _.get("data", []).get("src", ""),
            "pic": _.get("data", []).get("cover", ""),
        }
    except:
        data = music_err_url_rsp
    return data

# www.hhlqilongzhu.cn  kg
def _music_url_api_4(src, id, name='', artist='', quality='320k'):
    # 音乐源3
    src_table = {
        "kg": "dg_kugouSQ.php",
    }

    quality_table = {
        "128k": "128",
        "320k": "320",
        "flac": "flac"
    }

    if (src not in src_table or quality not in quality_table): return music_err_url_rsp

    # 音乐源1
    url = f"https://www.hhlqilongzhu.cn/api/{src_table[src]}?msg={name}-{artist}&n=1&type=json&quality={quality_table[quality]}"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
    }
    try:
        logger.info(f"request: {url}")
        _ = requests.get(url, headers=headers, timeout=3).json()
        data = {
            "code": 0,
            "msg": "success", 
            "url": _.get("music_url", ""),
            "pic": _.get("cover", ""),
        }
    except:
        data = music_err_url_rsp
    return data

# oiapi.net  kw
def _music_url_api_5(src, id, name='', artist='', quality='320k'):
    src_table = {
        "kw": {
            "api": "Kuwo",
            "quality": {
                "320k": "2",
                "flac": "1"
            }
        }
    }

    api = src_table.get(src, {}).get("api", "")
    quality_table = src_table.get(src, {}).get("quality", {})

    if (src not in src_table or quality not in quality_table): return music_err_url_rsp

    # 音乐源1
    url = f"http://oiapi.net/API/{api}?msg={name}-{artist}&n=1&br={quality_table[quality]}"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
    }
    try:
        logger.info(f"request: {url}")
        _ = requests.get(url, headers=headers, timeout=3).json()
        data = {
            "code": 0,
            "msg": "success", 
            "url": _.get("data", {}).get("url", ""),
            "pic": _.get("data", {}).get("picture", ""),
        }
    except:
        data = music_err_url_rsp
    return data

# api.leafone.cn  kw
def _music_url_api_6(src, id, name='', artist='', quality='320k'):
    src_table = {
        "kw": {
            "api": "kuwo",
            "quality": {
                "320k": "5",
                "flac": "6"
            }
        }
    }

    api = src_table.get(src, {}).get("api", "")
    quality_table = src_table.get(src, {}).get("quality", {})

    if (src not in src_table or quality not in quality_table): return music_err_url_rsp

    # 音乐源1
    url = f"https://api.leafone.cn/api/{api}?id={id}&type={quality_table[quality]}"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
    }
    try:
        logger.info(f"request: {url}")
        _ = requests.get(url, headers=headers, timeout=3).json()
        data = {
            "code": 0,
            "msg": "success", 
            "url": _.get("data", {}).get("url", ""),
            "pic": _.get("data", {}).get("cover", ""),
        }
    except:
        data = music_err_url_rsp
    return data

# api.xingzhige.com  tx
# 前面的全部失效，使用该接口，搜索歌曲
def _music_url_api_7(src, id, name='', artist='', quality='320k'):
    src_table = {
        "tx": "dg_kugouSQ.php",
        "wy": "dg_kugouSQ.php",
        "kg": "dg_kugouSQ.php",
        "kw": "dg_kugouSQ.php",
        "mg": "dg_kugouSQ.php",
    }

    quality_table = {
        "128k": "128",
        "320k": "320",
        "flac": "flac"
    }

    if (src not in src_table or quality not in quality_table): return music_err_url_rsp

    # 音乐源1
    url = f"https://www.hhlqilongzhu.cn/api/{src_table[src]}?msg={name}-{artist}&n=1&type=json&quality={quality_table[quality]}"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
    }
    try:
        logger.info(f"request: {url}")
        _ = requests.get(url, headers=headers, timeout=3).json()
        data = {
            "code": 0,
            "msg": "success", 
            "url": _.get("music_url", ""),
            "pic": _.get("cover", ""),
        }
    except:
        data = music_err_url_rsp
    return data

def music_url_api(src, id, name='', artist='', quality='320k'):
    api_fun = [
        _music_url_api_2,
        _music_url_api_3,
        _music_url_api_4,
        _music_url_api_5,
        _music_url_api_6,
        _music_url_api_7
    ]
    for api in api_fun:
        data = api(src, id, name, artist, quality)
        if (data.get("msg", "") == "success"):
            return data
    return music_err_url_rsp


cache_weather = {}
location = "113.96,22.57"
async def get_weather():
    global cache_weather
    qweather_key = config.get("qweather", {}).get("token", "")
    cache_weather_time = int(datetime.fromisoformat(cache_weather.get("now", {}).get("obsTime", "2025-01-17T21:10+08:00")).timestamp())
    if (cache_weather != {} and (time.time() - cache_weather_time) < 10 * 60):
        logger.info("get cache weather")
        return cache_weather
    
    async with aiohttp.ClientSession() as session:
        async with session.get(f"https://devapi.qweather.com/v7/weather/now?location={location}&key={qweather_key}") as response:
            data = await response.json()
    icon_url = f"http://gitee.com/bluetune/esp_sparkbot/raw/master/example/factory_demo_v1/weather/{data['now']['icon']}.png"
    data["now"].update({"icon_url": icon_url})
    cache_weather = data
    return data

async def get_qweather(query):
    async with aiohttp.ClientSession() as session:
        async with session.get(f"{query}") as response:
            return await response.json()

def _get_rand_music(src, list):
    logger.info(f"{src}_get_rand_music")
    try:
        music =  csv_read_list(list)
        # 读取随机行
        rand_row = random.choice(music)
        info = _audio_csv_2_json(rand_row)

        id = info.get("url")
        name = info.get("name")
        artist = info.get("artists")
        data = music_url_api(src, id, name=name, artist=artist, quality="320k")
        url = data.get("url").replace("https", "http")
        pic = data.get("pic", "").replace("https", "http")
        if (len(url)):
            info["url"] = url
            info["pic"] = pic
            return info
    except:
        logger.info("get err")
    return None

def get_rand_music():
    src_table = [
        ["tx", TX_PLAYLIST_HOT],
        ["wy", NETEASE_PLAYLIST_HOT],
        ["kg", KG_PLAYLIST_HOT],
        ["kw", KW_PLAYLIST_HOT],
    ]
    src = random.choice(src_table)
    music_info = _get_rand_music(src[0], src[1])
    if (music_info):
        r_data = {
            "code": 200,
            "data": music_info
        }
        return r_data
    return {"code": 404}

cache_music = {}
cache_time = 0

def update_music_cache():
    global cache_music
    global cache_time
    cache_music = get_rand_music()
    cache_time = int(time.time())

def get_rand_music_from_cache():
    global cache_music
    music = {}
    if ((time.time() - cache_time) < 3600 and cache_music != {}):
        music = cache_music
    if (music == {}):
        music = get_rand_music()
    # 创建线程更新缓存音乐
    cache_music = {}
    t = Thread(target=update_music_cache)
    t.start()
    logger.info(music)
    return music

def _is_name_same(name1, name2):
    name1 = name1.replace(" ", "").replace("/", ",").replace("&", ",").replace("、", ",").replace("（", "(").replace("）", ")").replace("ive版", "ive")
    name2 = name2.replace(" ", "").replace("/", ",").replace("&", ",").replace("、", ",").replace("（", "(").replace("）", ")").replace("ive版", "ive")
    song1 = name1.split("-")[0]
    song2 = name2.split("-")[0]
    singer1 = name1.split("-")[1].split(",")
    singer2 = name2.split("-")[1].split(",")
    if (song1 == song2):
        for i in singer1:
            if i in singer2:
                return True
    return False

def _get_local_music_name(name):
    music_path = config.get("music_path", "")
    for i in os.listdir(music_path):
        local_name = os.path.splitext(i)[0]
        if (_is_name_same(name, local_name)):
            return i
    return ""

def save_song_requests(info):
    line = [info["song"], info["src"], info["id"], info["mid"], ""]
    list = csv_read_list(song_req_cache_file)
    if not any(line[0] == i[0] for i in list):
        list.append(line)
        csv_write_list(song_req_cache_file, list, "w")

def get_local_music(info):
    logger.info(f"find music {info['song']}")
    save_song_requests(info)
    data = music_err_url_rsp
    local_name = _get_local_music_name(info["song"])
    if (local_name != ""):
        data = {
            "code": 0,
            "msg": "success", 
            "music_url": f"http://192.168.10.138:10074/files/music/{local_name}"
        }
    logger.info(f"rsp {data}")
    return data

def fm_get_cur_id_info(id):
    music_infos = []
    r = requests.get(f"https://webapi.qingting.fm/api/pc/radio/{id}", timeout=3)
    data = r.json()
    yesterday = (datetime.now() - timedelta(days=1)).strftime('%Y%m%d')
    if (data["album"] == []):
        return []
    pic = data["album"]["cover"]
    artists = data["album"]["title"]

    today = datetime.now().weekday()
    today = (today + 1) if (today + 1) <= 6 else 7  

    d_data = data["pList"][str(today)]
    for i in d_data:
        name = i["title"]
        start_time = i["start_time"].replace(":", "")
        end_time = i["end_time"].replace(":", "")
        url = f"http://lcache.qtfm.cn/cache/{yesterday}/{id}/{id}_{yesterday}_{start_time}_{end_time}_24_0.aac"
        music_infos.append({
            "name": name,
            "artists": artists,
            "url": url,
            "pic": pic
        })
    return music_infos

def fm_get_cur_id_info_csv(id):
    music_infos = []
    r = requests.get(f"https://webapi.qingting.fm/api/pc/radio/{id}", timeout=3)
    data = r.json()
    yesterday = (datetime.now() - timedelta(days=1)).strftime('%Y%m%d')
    if (data["album"] == []):
        return []
    pic = data["album"]["cover"]
    artists = data["album"]["title"]

    today = datetime.now().weekday()
    today = (today + 1) if (today + 1) <= 6 else 7  

    d_data = data["pList"][str(today)]
    for i in d_data:
        name = i["title"]
        start_time = i["start_time"].replace(":", "")
        end_time = i["end_time"].replace(":", "")
        url = f"http://lcache.qtfm.cn/cache/{yesterday}/{id}/{id}_{yesterday}_{start_time}_{end_time}_24_0.aac"
        music_infos.append([name, artists, url, pic])
    return music_infos

def get_rand_radio():
    ids = csv_read_list(FM_ALL_ID)
    music_infos = fm_get_cur_id_info(random.choice(ids)[0])
    music_info = random.choice(music_infos)
    if (music_info):
        r_data = {
            "code": 200,
            "data": music_info
        }
        return r_data
    return {"code": 404}

def get_favorite_radio():
    music_infos = csv_read_list(FM_PLAYLIST_HOT)
    music_info = _audio_csv_2_json(random.choice(music_infos))
    if (music_info):
        r_data = {
            "code": 200,
            "data": music_info
        }
        return r_data
    return {"code": 404}

def get_rand_mv():
    mv = _get_rand_music('wy', NETEASE_PLAYLIST_HOT).get("mv", "")
    return mv

def pic_resize(pic_url, w, h):
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0"
    }
    r = requests.get(pic_url,headers=headers, timeout=5)
    if (r.status_code == 200):
        img_io = BytesIO(r.content)
        img = Image.open(img_io)
        logger.info(f"{pic_url} {img.width}*{img.height} -> {w}*{h}")
        final_img = img

        if (w and h):
            # 原始尺寸和比例
            src_w, src_h = img.size
            src_ratio = src_w / src_h
            target_ratio = w / h

            # 计算调整尺寸
            if src_ratio > target_ratio:
                # 原图更宽，按目标高度缩放
                interim_h = h
                interim_w = int(interim_h * src_ratio)
            else:
                # 原图更高，按目标宽度缩放
                interim_w = w
                interim_h = int(interim_w / src_ratio)

            # 高质量缩放
            resized_img = img.resize((interim_w, interim_h), Image.LANCZOS)
            
            # 计算裁剪坐标
            left = (interim_w - w) // 2
            top = (interim_h - h) // 2
            right = left + w
            bottom = top + h
            
            # 执行裁剪
            final_img = resized_img.crop((left, top, right, bottom))
        
        img_out_io = BytesIO()
        if (img.format == "WEBP"):
            img.format = "JPEG"
        final_img.save(img_out_io, img.format)
        return img_out_io.getvalue(), img.format.lower()
    else:
        return None, None

def _rand_pic_api_1():
    ids = [1,2,4,5,6,7,8,9,10]
    return f"https://api.yyy001.com/api/image?id={random.choice(ids)}"

def _rand_pic_api_2():
    return "https://imgapi.cn/api.php"

def _rand_pic_api_3():
    return "https://v2.api-m.com/api/meinvpic?return=302"

def _rand_pic_api_4():
    url = "https://api.dwo.cc/api/imgs"
    r = requests.get(url, timeout=3).json()
    return r.get("数据", {}).get("pic")

def _rand_pic_api_5():
    return "https://api.suyanw.cn/api/meinv.php"

def get_rand_pic(width, height):
    apis = [
        # _rand_pic_api_1, # 不稳定
        _rand_pic_api_2,
        _rand_pic_api_3,
        _rand_pic_api_4,
        _rand_pic_api_5,
    ]
    pic_url = random.choice(apis)()
    logger.info(pic_url)
    return pic_resize(pic_url, width, height)

def music_api(src, params):
    id = params.get("id")
    mid = params.get("mid", "")
    quality = params.get("quality", ['320k'])
    data = music_url_api(src, id, mid, quality)
    return data

async def handle_request_api(req):
    logger.info(f"req {req.url}")
    src = req.match_info['src']
    query = req.query
    data = music_api(src, query)
    return web.json_response(data)

def get_today():
    url = "https://api.52vmy.cn/api/wl/today"
    r = requests.get(url, timeout=3)
    return r.json()

def get_yiyan():
    type = {
        "emo": "emo",
        "趣味笑话": "qwxh",
        "随机一言": "sj",
        "英汉语录": "yhyl",
        "爱情语录": "aiqing",
        "舔狗修养": "gt",
        "伤感语录": "shanggan",
        "舔狗日记": "tiangou",
        "社会语录": "shehui",
        "诗词": "sc1",
        "网易云": "wyy",
        "经典语录": "jdyl",
        "默认": "all",
        "一言": "yan",
        "人生话语": "rshy",
        "网易语录": "wyyl",
        "诗句名言": "sm",
        "我在人间凑数的日子": "renjian"
    }
    url = f"https://v1.xqapi.com/v1.php?type=json&y={random.choice(list(type.values()))}"
    r = requests.get(url, timeout=3).json()
    r["data"] = r.pop("text")
    return r

async def get_dailyhot():
    url = "https://api.cenguigui.cn/api/juhe/hotlist.php?type=weibo"
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            raw = await response.json()

    news = []
    for i in raw["data"]:
        hot = int(float(i["hot"].replace("万", ""))*10000)
        news.append({"title": i["title"], "hot": hot})
    data = {
        "code": 200,
        "updateTime": raw["update_time"],
        "data": news
    }
    return data

# 自定义的请求处理程序
async def handle_request(req):
    start_time = time.time()
    path = req.path
    query = req.query

    logger.info(f"req {req.url}")

    if (path == "/weather"):
        data = await get_weather()
        return web.json_response(data)
    elif (path == "/qweather"):
        data = await get_qweather(query)
        return web.json_response(data)
    elif (path == "/rand_music"):
        data = get_rand_music_from_cache()
        return web.json_response(data)
    elif (path == "/rand_radio" or path == "/rand_fm"):
        data = get_rand_radio()
        return web.json_response(data)
    elif (path == "/favorite_radio" or path == "/hot_fm"):
        data = get_favorite_radio()
        return web.json_response(data)
    elif (path == "/rand_mv"):
        data = get_rand_mv()
        if (data != ""):
            return web.HTTPFound(data)
        return web.HTTPFound("http://em.21dtv.com/songs/60009668.mkv")
    elif (path == "/rand_pic"):
        width = int(query.get('w', 0))
        height = int(query.get('h', 0))
        image,format = get_rand_pic(width, height)
        logger.info(f"{path} time: {(time.time() - start_time) * 1000: .0f} ms")
        if (image):
            return web.Response(body=image, content_type=f"image/{format}")
        else:
            return web.Response(status=404)
    elif (path == "/get_proxy"):
        proxy_pool = csv_read_list(PROXY_LIST)
        ip = random.choice(proxy_pool)[0]
        return web.Response(status=200, text=ip, content_type="text/plain")
    elif (path == "/pic_resize"):
        pic_url = query.get('url', "")
        width = int(query.get('w', 100))
        height = int(query.get('h', 100))
        image,format = pic_resize(pic_url, width, height)
        if (image):
            return web.Response(body=image, content_type=f"image/{format}")
        else:
            return web.Response(status=404)
    elif (path == "/today"):
        data = get_today()
        return web.json_response(data)
    elif (path == "/yiyan"):
        data = get_yiyan()
        return web.json_response(data)
    elif (path == "/music"):
        info = {"song": "", "src": "", "id": "", "mid": ""}
        info["song"] = query.get('msg', "")
        info["src"] = query.get('src', "")
        info["id"] = query.get('id', "")
        info["mid"] = query.get('mid', "")
        data = get_local_music(info)
        return web.json_response(data)
    elif (path == "/dailyhot"):
        data = await get_dailyhot()
        return web.json_response(data)

api_get_param = ['/qweather', '/weather', '/rand_music', '/rand_radio', '/rand_fm', '/favorite_radio',
                 '/hot_fm', '/get_proxy', '/pic_resize', '/api', '/rand_mv', '/rand_pic', "/today", 
                 '/yiyan', '/music', '/dailyhot']

def main_app():
    app = web.Application()
    for p in api_get_param:
        app.router.add_get(p, handle_request)

    app.router.add_get('/api/{src}', handle_request_api)

    web.run_app(app, host='0.0.0.0', port=8888)

if __name__ == "__main__":
    logger.info("start server")
    t = Thread(target=main_app, daemon=True)
    t.start()

    try:
        while (1):
            time.sleep(3600)
    except KeyboardInterrupt:
        logger.info("KeyboardInterrupt, exit")
